﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Diagnostics;

namespace Me.Prettyprint.Cassandra.Utils
{
    /// <summary>
    /// As of July 2013, .Net doesn't have an equivalent of Java's Atmoic classes,
    /// so we need our own. It's following the .Net best practices and uses
    /// Thread.MemoryBarrier() to get the equivalent of Java atomics.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class AtomicValue<T>
    {
        T _val;
        public AtomicValue(T val)
        {
            this._val = val;
            Thread.MemoryBarrier();
        }
        public T Value
        {
            get
            {
                    Thread.MemoryBarrier();
                    var r = this._val;
                    Thread.MemoryBarrier();
                    return r;
            }
            set
            {
                    Thread.MemoryBarrier();
                    this._val = value;
                    Thread.MemoryBarrier();
            }
        }
    }

    internal class AtomicInt
    {
        int _intVal = 0;
        public AtomicInt(int val)
        {
            this._intVal = val;
        }
        public int Value
        {
            get
            {
                return this._intVal;
            }
            set
            {
                Thread.MemoryBarrier();
                this._intVal = value;
                Thread.MemoryBarrier();
            }
        }
        public int Increment()
        {
            Interlocked.Increment(ref _intVal);
            return _intVal;
        }
        public int Decrement()
        {
            Interlocked.Decrement(ref _intVal);
            return _intVal;
        }
    }

    internal class AtomicLong
    {
        long _longVal = 0L;
        public AtomicLong(long val)
        {
            this._longVal = val;
        }
        public long Value
        {
            get
            {
                return this._longVal;
            }
            set
            {
                Thread.MemoryBarrier();
                this._longVal = value;
                Thread.MemoryBarrier();
            }
        }
        public long Increment()
        {
            Interlocked.Increment(ref _longVal);
            return _longVal;
        }
        public long Decrement()
        {
            Interlocked.Decrement(ref _longVal);
            return _longVal;
        }
        public long AddAndGet(long value)
        {
            Interlocked.Add(ref _longVal, value);
            return _longVal;
        }
    }

    internal class AtomicArray<T>
    {
        readonly T[] _arr = null;
        public AtomicArray(int size)
        {
            _arr = new T[size];
            Thread.MemoryBarrier();
        }
        public T this[int idx]
        {
            get
            {
                    Thread.MemoryBarrier();
                    var r = this._arr[idx];
                    Thread.MemoryBarrier();
                    return r;
            }
            set
            {
                    Thread.MemoryBarrier();
                    _arr[idx] = value;
                    Thread.MemoryBarrier();
            }
        }
    }

    internal class Guarded<T>
    {
        T _val;

        void AssureLocked()
        {
            if (Monitor.TryEnter(this))
                Monitor.Exit(this);
            else
                throw new System.Threading.SynchronizationLockException();
        }
        
        public Guarded(T val)
        {
            this._val = val;
            Thread.MemoryBarrier();
        }
        public T Value
        {
            get
            {
                AssureLocked();
                return _val;
            }
            set
            {
                AssureLocked();
                _val = value;
            }
        }
    }

}